

namespace Stee.CAP8.Service.Shared.Converters
{
    using System;
    using System.Collections.Generic;
    using System.Reflection;
    using System.Text.Json;
    using Google.Protobuf;
    using Google.Protobuf.Reflection;

    public static class ProtobufConvertor
    {
        /// <summary>
        /// Convert Protobuf message to model according to the attribute name
        /// </summary>
        /// <typeparam name="T">data model type</typeparam>
        /// <param name="probufMsg">protobuf message</param>
        /// <returns>data model</returns>
        public static T ToObject<T>(IMessage probufMsg) where T : new()
        {
            if (probufMsg == null)
            {
                throw new ArgumentNullException(nameof(probufMsg));

            }

            var lstProperties = typeof(T).GetProperties();
            var mapAttribute = new Dictionary<string, PropertyInfo>();

            foreach (var property in lstProperties)
            {
                mapAttribute.Add(property.Name, property);
            }

            var descriptor = probufMsg.Descriptor;

            if (descriptor == null)
            {
                throw new  InvalidOperationException($"{probufMsg.Descriptor} is null.");
            }

            var obj = new T();

            foreach (var field in descriptor.Fields.InDeclarationOrder())
            {
                if (mapAttribute.ContainsKey(field.Name))
                {
                    var property = mapAttribute[field.Name];
                    property.SetValue(obj, field.Accessor.GetValue(probufMsg));
                }
            }

            return obj;
        }

        /// <summary>
        /// Convert the data model to protobuf message
        /// </summary>
        /// <typeparam name="T">Data Model type</typeparam>
        /// <typeparam name="TResult">Protobuf Message type</typeparam>
        /// <param name="obj">Data Model</param>
        /// <returns>Protobuf Message</returns>
        public static TResult ToProtobufMesssage<T, TResult>(T obj) where TResult : IMessage
        {
            if (obj == null)
            {
                throw new ArgumentNullException(nameof(obj));
            }

            var strJson = JsonSerializer.Serialize<T>(obj);
            var descriptor = (MessageDescriptor)typeof(TResult)
                .GetProperty("Descriptor", BindingFlags.Public | BindingFlags.Static)
                .GetValue(null, null);

            return (TResult)descriptor.Parser.ParseJson(strJson);
        }
    }
}
